package linker

import (
	"bytes"
	"debug/elf"
	"math"
	"risc-v/ld/pkg/utils"
)

type ObjectFile struct {
	InputFile
	SymbolTableSection    *SectionHeader  // 符号表的 SectionHeader
	SymbolTabShndxSection []uint32        // 存储 ELFSymbol.Shndx 大于 uint16 最大值的 ELFSymbol 的 Shndx
	Sections              []*InputSection // 此 ObjectFile 包含的多个 Section
	MergeableSections     []*MergeableSection
}

// NewObjectFile ObjectFile 构造函数
func NewObjectFile(f *File, isAlive bool) *ObjectFile {
	o := &ObjectFile{
		InputFile:          NewInputFile(f),
		SymbolTableSection: nil,
	}

	o.IsAlive = isAlive

	return o
}

// Parse 解析 ObjectFile 提取有效信息到 Context 中
func (o *ObjectFile) Parse(ctx *Context) {
	// 找到符号表
	o.SymbolTableSection = o.FindSection(uint32(elf.SHT_SYMTAB))
	if o.SymbolTableSection != nil {
		// SHT_SYMTAB 类型 Section 的 Info 字段表示 	One greater than the symbol table index of the last local symbol
		// 即 第一个 Global 符号
		// more detail: https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html#sh_link
		o.FirstGlobalSymbol = int(o.SymbolTableSection.Info)
		o.FillUpELFSymbol(o.SymbolTableSection)
		o.SymbolStrTable = o.GetBytesFromSectionIndex(int64(o.SymbolTableSection.Link))
	}
	o.initializeSections(ctx)          // 初始化 Section
	o.initializeSymbols(ctx)           // 初始化符号表
	o.initializeMergeableSections(ctx) // 初始化可合并节
	o.SkipELFFrameSections()           //跳过处理 .eh_frame 节

}

// ResolveSymbol 填充 全局 Symbol 的信息
// 如 File Value 等
func (o *ObjectFile) ResolveSymbol() {

	for i := o.FirstGlobalSymbol; i < len(o.ElfSymbols); i++ {
		sym := o.Symbols[i]
		elfsym := &o.ElfSymbols[i]

		if elfsym.IsUndef() {
			continue
		}

		// 获取 elfsym 对应的 InputSection
		var inputSection *InputSection
		if !elfsym.IsAbs() {
			inputSection = o.GetSection(elfsym, i)
			if inputSection == nil {
				continue
			}
		}

		// 一个符号可以存在于多个文件中
		// 这里设置 符号所属的文件和 section 的时候选取第一个读入的 文件和 Section
		if sym.File == nil {
			sym.File = o
			sym.SetInputSection(inputSection)
			sym.Value = elfsym.Value
			sym.SymbolIndex = i
		}
	}
}

// GetSection 根据 ELFSymbol 和 其在符号表中的 index 获取 ObjectFile 中的 InputSection
// 如果 Section 为空则返回 nil
func (o *ObjectFile) GetSection(elfsym *ELFSymbol, index int) *InputSection {
	//TODO
	return o.Sections[o.GetSectionHeaderTabIndex(elfsym, index)]
}

// MarkLiveObjects 标记 ObjectFile.Alive 选项
func (o *ObjectFile) MarkLiveObjects(feeder func(file *ObjectFile)) {
	utils.Assert(o.IsAlive)

	// 遍历所有全局符号 递归标记 alive 文件
	for i := o.FirstGlobalSymbol; i < len(o.ElfSymbols); i++ {
		sym := o.Symbols[i]
		elfsym := &o.ElfSymbols[i]

		if sym.File == nil {
			continue
		}

		// 该符号未定义 并且所在的文件没有被标记未 Alive
		if elfsym.IsUndef() && !sym.File.IsAlive {
			sym.File.IsAlive = true
			feeder(sym.File)
		}
	}
}

// FillUpSymbolTabShndxSection  通过参数 SectionHeader 填充 ObjectFile 的 SymbolTabShndxSection 成员
func (o *ObjectFile) FillUpSymbolTabShndxSection(s *SectionHeader) {
	byteStream := o.GetBytesFromSectionHeader(s)
	o.SymbolTabShndxSection = utils.ReadSlice[uint32](byteStream, 4)
}

// GetSectionHeaderTabIndex 根据提供的 ELFSymbol 和 index 获取对应节在 SectionHeaderTab 中的索引
// index : ELFSymbol 在符号表中的索引
// 因为有些 符号的 Shndx 字段会发生溢出
func (o *ObjectFile) GetSectionHeaderTabIndex(sym *ELFSymbol, index int) int64 {
	utils.Assert(index >= 0 && index < len(o.ElfSymbols))

	if sym.Shndx == uint16(elf.SHN_XINDEX) {
		return int64(o.SymbolTabShndxSection[index])
	}

	return int64(sym.Shndx)
}

// ClearSymbols 清空 ObjectFile 中的无用的 符号
func (o *ObjectFile) ClearSymbols() {
	// 从 Global 符号开始
	for _, sym := range o.Symbols[o.FirstGlobalSymbol:] {
		// 清除的是符号定义的节 而不是符号出现的节
		if sym.File == o {
			sym.Clear()
		}
	}
}

// RegisterSectionPieces 处理合并节 符号关联相关信息
func (o *ObjectFile) RegisterSectionPieces() {
	for _, m := range o.MergeableSections {
		if m == nil {
			continue
		}

		m.Fragments = make([]*SectionFragment, 0, len(m.Strings))
		for i := 0; i < len(m.Strings); i++ {
			m.Fragments = append(m.Fragments, m.Parent.Insert(m.Strings[i], uint32(m.P2Align)))
		}
	}

	for i := 1; i < len(o.ElfSymbols); i++ {
		sym := o.Symbols[i]
		elfSym := &o.ElfSymbols[i]

		if elfSym.IsAbs() || elfSym.IsUndef() || elfSym.IsCommon() {
			continue
		}
		m := o.MergeableSections[o.GetSectionHeaderTabIndex(elfSym, i)]
		if m == nil {
			continue
		}

		frag, fragOffset := m.GetFragment(uint32(elfSym.Value))
		if frag == nil {
			utils.Fatal("bad symbol value")
		}

		sym.SetSectionFragment(frag)
		sym.Value = uint64(fragOffset)
	}
}

func (o *ObjectFile) RegisterSectionPiecesLL() {
	for _, m := range o.MergeableSections {
		if m == nil {
			continue
		}

		m.Fragments = make([]*SectionFragment, 0, len(m.Strings))
		for i := 0; i < len(m.Strings); i++ {
			m.Fragments = append(m.Fragments,
				m.Parent.Insert(m.Strings[i], uint32(m.P2Align)))
		}
	}

	for i := 1; i < len(o.ElfSymbols); i++ {
		sym := o.Symbols[i]
		esym := &o.ElfSymbols[i]

		if esym.IsAbs() || esym.IsUndef() || esym.IsCommon() {
			continue
		}

		m := o.MergeableSections[o.GetSectionHeaderTabIndex(esym, i)]
		if m == nil {
			continue
		}

		frag, fragOffset := m.GetFragment(uint32(esym.Value))
		if frag == nil {
			utils.Fatal("bad symbol value")
		}
		sym.SetSectionFragment(frag)
		sym.Value = uint64(fragOffset)
	}
}

// initializeSections 初始化 ObjectFile 中 Sections(InputSection 类型) 成员
func (o *ObjectFile) initializeSections(ctx *Context) {
	o.Sections = make([]*InputSection, len(o.SectionHeaders))
	size := len(o.SectionHeaders)

	for i := 0; i < size; i++ {
		sectionHeader := &o.SectionHeaders[i]
		switch elf.SectionType(sectionHeader.Type) {
		case elf.SHT_GROUP, elf.SHT_SYMTAB, elf.SHT_STRTAB, elf.SHT_REL, elf.SHT_RELA, elf.SHT_NULL:
			break
		case elf.SHT_SYMTAB_SHNDX:
			o.FillUpSymbolTabShndxSection(sectionHeader)
		default:
			// 保存 Section 到 ObjectFile 的 InputSection
			name := GetSectionEntryName(o.InputFile.SectionHeaderStrTable, sectionHeader.Name)
			o.Sections[i] = NewInputSection(ctx, name, o, uint32(i))
		}
	}

	// 通过 SectionHeader 的 type 字段来寻找 重定位节
	for i := 0; i < len(o.Sections); i++ {
		shdr := &o.InputFile.SectionHeaders[i]

		if shdr.Type != uint32(elf.SHT_RELA) {
			continue
		}

		utils.Assert(shdr.Info < uint32(len(o.Sections)))
		if target := o.Sections[shdr.Info]; target != nil {
			utils.Assert(target.RelSectionIndex == math.MaxUint32)
			target.RelSectionIndex = uint32(i)
		}
	}
}

// initializeSymbols 根据已有信息分配空间并补充已知信息
// 需要配合 ObjectFile.Resolve() 方法使用
func (o *ObjectFile) initializeSymbols(ctx *Context) {
	if o.SymbolTableSection == nil {
		return
	}

	// 分配空间
	o.LocalSymbols = make([]Symbol, o.FirstGlobalSymbol)

	for i := 0; i < len(o.LocalSymbols); i++ {
		o.LocalSymbols[i] = *NewSymbol("")
	}

	// 第一个符号， 第一个符号为特殊符号
	// 忽略处理
	o.LocalSymbols[0].File = o

	for i := 1; i < len(o.LocalSymbols); i++ {
		elfsym := &o.ElfSymbols[i]
		sym := &o.LocalSymbols[i]
		sym.Name = GetSectionEntryName(o.SymbolStrTable, elfsym.Name)
		sym.File = o
		sym.Value = elfsym.Value
		sym.SymbolIndex = i

		// 设置符号所属的  InputSection  同时 Abs 类型不用设置 InputSection
		if !elfsym.IsAbs() {
			sym.SetInputSection(o.Sections[o.GetSectionHeaderTabIndex(elfsym, i)])
		}
	}

	// 分配空间
	o.Symbols = make([]*Symbol, len(o.ElfSymbols))

	for i := 0; i < len(o.LocalSymbols); i++ {
		o.Symbols[i] = &o.LocalSymbols[i]
	}

	for i := len(o.LocalSymbols); i < len(o.ElfSymbols); i++ {
		elfsym := &o.ElfSymbols[i]
		name := GetSectionEntryName(o.SymbolStrTable, elfsym.Name)
		o.Symbols[i] = GetSymbolByName(ctx, name)
	}

}

func (o *ObjectFile) initializeMergeableSections(ctx *Context) {
	// 虽然有的 Section 不是 MergeableSection 但是这样有利于代码的实现
	o.MergeableSections = make([]*MergeableSection, len(o.Sections))
	for i := 0; i < len(o.Sections); i++ {
		inSec := o.Sections[i]
		// section 是alive 并且是可合并的
		if inSec != nil && inSec.IsAlive && inSec.SectionHeader().Flags&uint64(elf.SHF_MERGE) != 0 {
			o.MergeableSections[i] = splitSection(ctx, inSec)
			inSec.IsAlive = false // 这个 InputSection 已经被拆成 MergeableSection 这里不再需要
		}
	}

}

// findNull 找到对齐为 entrySize 的终止符的下标
func findNull(data []byte, entrySize int) int {
	if entrySize == 1 {
		return bytes.Index(data, []byte{0})
	}

	for i := 0; i <= len(data)-entrySize; i += entrySize {
		byteStream := data[i : i+entrySize]
		if utils.AllZeros(byteStream) {
			return i
		}
	}

	return -1
}

// splitSection 把一个 InputSection 分割成 MergeableSection
// 读入section 中的数据，以string的形式保存到 MergeableSection 中
// 同时标记对应的偏移
func splitSection(ctx *Context, inSection *InputSection) *MergeableSection {
	m := &MergeableSection{}
	sectionHeader := inSection.SectionHeader()

	// 在这里 MergeableSection 和 MergedSection 关联起来
	m.Parent = GetMergedSectionInstance(ctx, inSection.Name(), sectionHeader.Type, sectionHeader.Flags)
	m.P2Align = inSection.P2Align

	data := inSection.Contents
	offset := uint64(0)

	// 节类型为 string
	if sectionHeader.Flags&uint64(elf.SHF_STRINGS) != 0 {
		for len(data) > 0 {
			// 这里的 EntrySize 可以理解为一个对齐量
			// 最小的存储单位的大小
			end := findNull(data, int(sectionHeader.EntrySize))
			if end == -1 {
				utils.Fatal("string is not null terminated")
			}

			size := uint64(end) + sectionHeader.EntrySize
			substr := data[:size]
			data = data[size:]
			m.Strings = append(m.Strings, string(substr))
			m.FragOffsets = append(m.FragOffsets, uint32(offset))
			offset += size
		}
	} else {
		// 处理常量类型
		if uint64(len(data))%sectionHeader.EntrySize != 0 {
			utils.Fatal("section size is not multiple of entry size")
		}
		for len(data) > 0 {
			substr := data[:sectionHeader.EntrySize]
			data = data[sectionHeader.EntrySize:]
			m.Strings = append(m.Strings, string(substr))
			m.FragOffsets = append(m.FragOffsets, uint32(offset))
			offset += sectionHeader.EntrySize
		}
	}

	return m
}

// SkipELFFrameSections 跳过 .eh_frame 节
// 关于本节的具体内容 可以参考 README 文档中的参考链接，这节主要是用来保存一些调试相关的信息
func (o *ObjectFile) SkipELFFrameSections() {
	for _, isec := range o.Sections {
		if isec != nil && isec.IsAlive && isec.Name() == ".eh_frame" {
			isec.IsAlive = false
		}
	}
}

func (o *ObjectFile) ScanRelocations() {
	for _, isec := range o.Sections {
		if isec != nil && isec.IsAlive &&
			isec.SectionHeader().Flags&uint64(elf.SHF_ALLOC) != 0 {
			isec.ScanRelocations()
		}
	}
}
